#include "HTTP_Request.hpp"
#include <sstream>
#include <map>
#include <unordered_map>
#include <fstream>

using std::fstream;
using std::string;
using std::unordered_map;

// 根据HTTP请求中的URL指示的文件名，来获取HTTP响应中的content-type
unordered_map<string, string> content_type_table = {
    {".html", "text/html"},
    {".xml", "text/xml"},
    {".xhtml", "application/xhtml+xml"},
    {".txt", "text/plain"},
    {".rtf", "application/rtf"},
    {".pdf", "application/pdf"},
    {".word", "application/msword"},
    {".png", "image/png"},
    {".gif", "image/gif"},
    {".jpg", "image/jpeg"},
    {".jpeg", "image/jpeg"},
    {".au", "audio/basic"},
    {".mpeg", "video/mpeg"},
    {".mpg", "video/mpeg"},
    {".avi", "video/x-msvideo"},
    {".gz", "application/x-gzip"},
    {".tar", "application/x-tar"},
    {".css", "text/css"}};

int HTTP_Request::HTTP_Reccive_Request(){

    // 因为是边缘触发模式，这里需要一直读，直到EAGAIN
    request_len = recv(client_fd, buffer, BUF_SIZE, 0);

    //cout << buffer << endl;

    return request_len;
}

void HTTP_Request::HTTP_Parse_Request(){
    parse_state = PARSE_METHOD;
    preBlankPos = buffer;
    curPos = buffer;

    // cout << "Parsing..." << endl;
    /* 未添加异常处理，既默认所有的HTTP请求都是合法的 */
    // 解析请求行
    parse_request_line();
    // 解析请求头部
    // 当前请求头部只需要请求找到key为Connection那一行即可。
    parse_request_head();
    init_response();
    string filename = "./src_file";

    // 解析URL
    parse_url(filename);

    // 组装HTTP响应
    construct_response(filename);
}
void HTTP_Request::init_response(){
    // keep_alive在初始化时已经设置过了
    modified = true;
    status_code = 200;  // 默认成功，错误之后会修改
}

void HTTP_Request::construct_response(string& filename){
    if(status_code == 404){
        filename = "./src_file/home/NotFound.html";
    }

    char response_buffer[MAX_LINE];
    struct stat file_info;
    stat(filename.c_str(), &file_info);  // 获取文件信息
    
    //char filename[SHORT_LINE];
    sprintf(response_buffer, "HTTP/1.1 %d %s\r\n", status_code, get_short_message());

    // 如果设置了长连接
    if(keep_alive){
        // 后面的response_buffer是为了把内容追加过去，否则会覆盖
        sprintf(response_buffer, "%sConnection: keep-alive\r\n", response_buffer);
        // 长连接的默认超时时间为500ms，即500ms后，如果网络端还要发请求，只能重新建立TCP连接
        sprintf(response_buffer, "%sKeep-Alive: timeout=%d\r\n", response_buffer, TIMEOUT_DEFAULT);
    }
    if(modified){
        sprintf(response_buffer, "%sContent-type: %s\r\n", response_buffer, content_type.c_str());
        sprintf(response_buffer, "%sContent-Length: %zu\r\n", response_buffer, file_info.st_size);
    }

    // 空行
    sprintf(response_buffer, "%s\r\n", response_buffer);

    // 打开文件
    int src_fd = open(filename.c_str(), O_RDONLY, 0);
    // 建立内存映射，返回的src_addr指向地址
    void *src_addr = mmap(NULL, file_info.st_size, PROT_READ, MAP_PRIVATE, src_fd, 0);
    // 添加到内容
    strncat(response_buffer, (char *)src_addr, file_info.st_size);
    close(src_fd);
    int sned_res = send(client_fd, response_buffer, MAX_LINE, 0);
    if(sned_res < 0){
        perror("Send Failed");
    }
    munmap(src_addr, file_info.st_size);
}

const char* HTTP_Request::get_short_message(){
    if(status_code == 200){
        return "OK";
    }
    if(status_code == 404){
        return "Not Found";
    }
}
void HTTP_Request::parse_url(string &filename){
    // 当前都是本地地址，URL一上来就是目录, 当前先做最简单的吧
    char *ptr = URL_end;

    // 获取地址,从右往左第一个/
    while(ptr >= URL_begin){
        if(*ptr == '/'){
            filename += string(URL_begin, ptr - URL_begin);
            break;
        }
        --ptr;
    }
    filename += "/";
    // 获取文件名，从最后一个/到最后，（其实应该到问号）
    filename += string(ptr + 1, URL_end - ptr);

    // 获取文件类型 找到最后一个.的尾置
    ptr = URL_end;
    while(ptr >= URL_begin){
        if(*ptr == '.'){
            content_type = content_type_table[string(ptr, URL_end - ptr + 1)];
            break;
        }
        --ptr;
    }

    // 查看是否存在请求的文件
    fstream file(filename);
    if(file.is_open()){
        status_code = 200;
    }
    else{
        status_code = 404;
    }
    file.close();
}
void HTTP_Request::parse_request_head(){
    while(*curPos != '\0'){
        // 找到了一个key，直接获取后面的value
        if(*curPos == ':'){
            string key = string(preBlankPos, curPos - preBlankPos);
            curPos += 2;
            preBlankPos = curPos;
            while(*curPos != '\r'){
                ++curPos;
            }
            string value = string(preBlankPos, curPos - preBlankPos);
            curPos += 2;
            preBlankPos = curPos;
            if(*curPos == '\r'){
                parse_state = PARSE_BODY;
                break;
            }
            if(key == "Connection"){
                if (value == "Keep-Alive")
                {
                    keep_alive = true;
                }
                else{
                    keep_alive = false;
                }
                //缺少处理函数
            }
            //cout << key << " " << value << endl;
        }
        else{
            ++curPos;
        }
    }
}

void HTTP_Request::parse_request_line(){
    while (*curPos != '\0')
    {
        if(*curPos == ' ' || *curPos == '\r'){
            switch(parse_state)
            {
                case PARSE_METHOD:
                    // 相等时strncmp返回0
                    if(strncmp("GET", preBlankPos, curPos - preBlankPos - 1) == 0){
                        request_method = METHOD_GET;
                    }
                    else if(strncmp("POST", preBlankPos, curPos - preBlankPos - 1) == 0){
                        request_method = METHOD_POST;
                    }
                    else{
                        request_method = METHOD_NOT_SUPPORT;
                    }
                    preBlankPos = curPos;
                    parse_state = PARSE_URL;
                    break;

                case PARSE_URL:
                    //URL = string(preBlankPos + 1, curPos - preBlankPos - 1);
                    URL_begin = preBlankPos + 1;
                    URL_end = curPos - 1;
                    preBlankPos = curPos;
                    parse_state = PARSE_PROCOTOL_VERSION;
                    break;

                case PARSE_PROCOTOL_VERSION:
                    if(strncmp("HTTP/1.1", preBlankPos + 1, curPos - preBlankPos - 1) == 0){
                        protocol_version = HTTP_1_1;
                    }
                    else if(strncmp("HTTP/1.0", preBlankPos + 1, curPos - preBlankPos - 1) == 0){
                        protocol_version = HTTP_1_0;
                    }
                    else{
                        protocol_version = NOT_SUPPORT_PROTOCOL;
                    }
                    parse_state = PARSE_HEADER;
                    break;
                }
        }
        if(*curPos == '\r'){
            ++curPos;
        }
        ++curPos;
        if(parse_state == PARSE_HEADER){
            preBlankPos = curPos;
            break;
        }
    }
}
